package edu.hdu.hangzhe.main;

import edu.hdu.hangzhe.bytecode.domain.*;
import edu.hdu.hangzhe.core.BytecodeParser;
import edu.hdu.hangzhe.utils.ABILUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class PatternGenerator {

    /**
     * 生成ABIL Pattern
     * @param abil abil
     * @return ArrayList<ABI>
     */
    public static ArrayList<ABI> genABILPattern(ArrayList<ABI> abil) {
        HashMap<String, String> recordMap = new HashMap<>();
        int num = 0;
        return genABILPattern(abil, recordMap, num);
    }
    private static ArrayList<ABI> genABILPattern(ArrayList<ABI> abil, HashMap<String, String> recordMap, int num) {

        String prefix = "s";
        for (ABI abi: abil) {
            if (ABILUtils.isVarIns(abi)) {
                String alpha = recordMap.get(abi.value);
                if (alpha == null) {
                    String slot = prefix + num;
                    recordMap.put(abi.value, slot);
                    ++num;
                    alpha = slot;
                }
                abi.setSlot(alpha);
            } else if (ABILUtils.isLDCIns(abi)) {
                String alpha = recordMap.get(abi.value);
                if (alpha == null) {
                    String slot = prefix + num;
                    recordMap.put(abi.value, slot);
                    ++num;
                    alpha = slot;
                }
                abi.setSlot(alpha);
            } else if (ABILUtils.isJumpIns(abi)) {
                String v = abi.value;
                String pattern = "ABI\\{inst='([\\w]+)', value='([\\s\\S]*)'}";
                Pattern r = Pattern.compile(pattern);
                Matcher m = r.matcher(v);
                if (m.find()) {
                    String _ = m.group(0);
                    String inst = m.group(1);
                    String val = m.group(2);
                    if (val.equals("null")) {
                        abi.setSlot(null);
                        continue;
                    }
                    ABI tmp = new ABI(inst, val);
                    ArrayList<ABI> tmpList = new ArrayList<>();
                    tmpList.add(tmp);
                    genABILPattern(tmpList, recordMap, num);
                    tmp = tmpList.get(0);
                    abi.setSlot(tmp.toString());
                } else {
                    System.err.println("error: format error1");
                }

            } else if (ABILUtils.isGETPUTIns(abi)) {
                String pattern = "([/\\w]+)\\.(\\w+)";
                Pattern r = Pattern.compile(pattern);
                Matcher m = r.matcher(abi.value);
                if (m.find()) {
                    String _ = m.group(0);
                    String name = m.group(1);
                    String method = m.group(2);
                    String alpha = recordMap.get(name);
                    if (alpha == null) {
                        String slot = prefix + num;
                        recordMap.put(name, slot);
                        ++num;
                        alpha = slot;
                    }
                    name = alpha;
                    alpha = recordMap.get(method);
                    if (alpha == null) {
                        String slot = prefix + num;
                        recordMap.put(method, slot);
                        ++num;
                        alpha = slot;
                    }
                    method = alpha;
                    abi.setSlot(name + "." + method);
                } else {
                    System.err.println("error: format error");
                }
            } else if (ABILUtils.isInvokeIns(abi)) {
                String pattern = "([_/\\w]+)\\.([_<>$\\w]+)\\(([;/_$\\[\\w]*)\\)([;/_$\\[\\w]+)";
                Pattern r = Pattern.compile(pattern);
                Matcher m = r.matcher(abi.value);
                if (m.find()) {
                    String _ = m.group(0);
                    String name = m.group(1);
                    String method = m.group(2);
                    String args = m.group(3);
                    String returnType = m.group(4);
                    String alpha = recordMap.get(name);
                    if (alpha == null) {
                        String slot = prefix + num;
                        recordMap.put(name, slot);
                        ++num;
                        alpha = slot;
                    }
                    name = alpha;
                    alpha = recordMap.get(method);
                    if (alpha == null) {
                        String slot = prefix + num;
                        recordMap.put(method, slot);
                        ++num;
                        alpha = slot;
                    }
                    method = alpha;

                    abi.setSlot(name + "." + method + ABILUtils.simplifyType(args, returnType));
                } else {
                    System.err.println("error: format error2");
                }
            } else {
                abi.setSlot(null);
            }
        }
        return abil;
    }


    /**
     * 生成ABIL Edit Patterns
     * @param abilEdit abil edit
     * @return ABILEdit
     */
    public static ABILEdit genABILEditPattern(ABILEdit abilEdit) {
        HashMap<String, String> recordMap = new HashMap<>();
        int num = 0;
        ArrayList<ABI> abilOut = genABILPattern(abilEdit.getABIL(), recordMap, num);
        for (OP op : abilEdit.getOps()) {
            ArrayList<ABI> info = op.getInfo();
            if (info == null) continue;
            genABILPattern(info, recordMap, num);
        }
        return abilEdit;
    }
}
